/**
 *******************************************************************************
 * @file  wm8731.c
 * @brief This midware file provides firmware functions to manage the codec
 *        component library for wm8731.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2020-06-12       yangjp          First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "wm8731.h"
#include "hc32f4a0_utility.h"

/**
 * @addtogroup BSP
 * @{
 */

/**
 * @addtogroup Components
 * @{
 */

/**
 * @defgroup WM8731 Codec WM8731
 * @{
 */

#if (BSP_WM8731_ENABLE == DDL_ON)

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/**
 * @defgroup WM8731_Local_Macros WM8731 Local Macros
 * @{
 */

/* WM8731 volume convert */
#define WM8731_INPUT_VOL_CONV(__VOL__)          (((__VOL__) >= 100U) ? 31U : ((uint8_t)((((uint32_t)__VOL__) * 31U) / 100U)))
#define WM8731_OUTPUT_VOL_CONV(__VOL__)         (((__VOL__) >= 100U) ? 80U : ((uint8_t)((((uint32_t)__VOL__) * 80U) / 100U)))

/**
 * @defgroup WM8731_Check_Parameters_Validity WM8731 Check Parameters Validity
 * @{
 */
#define IS_WM8731_INPUT_DEVICE(x)                                              \
(   ((x) == WM8731_INPUT_DEVICE_NONE)           ||                             \
    ((x) == WM8731_INPUT_DEVICE_MICROPHONE)     ||                             \
    ((x) == WM8731_INPUT_DEVICE_LINE))

#define IS_WM8731_OUTPUT_DEVICE(x)                                             \
(   ((x) == WM8731_OUTPUT_DEVICE_NONE)          ||                             \
    ((x) == WM8731_OUTPUT_DEVICE_LINE)          ||                             \
    ((x) == WM8731_OUTPUT_DEVICE_HEADPHONE)     ||                             \
    ((x) == WM8731_OUTPUT_DEVICE_BOTH))

#define IS_WM8731_OUTPUT_SRC(x)                                                \
(   ((x) == WM8731_OUTPUT_SRC_LINE)             ||                             \
    ((x) == WM8731_OUTPUT_SRC_DAC)              ||                             \
    ((x) == WM8731_OUTPUT_SRC_MICROPHONE))

#define IS_WM8731_AUDIO_FREQ(x)                                                \
(   ((x) == WM8731_AUDIO_FREQ_8K)               ||                             \
    ((x) == WM8731_AUDIO_FREQ_32K)              ||                             \
    ((x) == WM8731_AUDIO_FREQ_48K)              ||                             \
    ((x) == WM8731_AUDIO_FREQ_96K))

#define IS_WM8731_DATA_FORMAT(x)                                               \
(   ((x) == WM8731_DATA_FORMAT_LSB)             ||                             \
    ((x) == WM8731_DATA_FORMAT_MSB)             ||                             \
    ((x) == WM8731_DATA_FORMAT_PHILLIPS)        ||                             \
    ((x) == WM8731_DATA_FORMAT_DSP))

#define IS_WM8731_DATA_LEN(x)                                                  \
(   ((x) == WM8731_DATA_LEN_16BIT)              ||                             \
    ((x) == WM8731_DATA_LEN_20BIT)              ||                             \
    ((x) == WM8731_DATA_LEN_24BIT)              ||                             \
    ((x) == WM8731_DATA_LEN_32BIT))

#define IS_WM8731_VOLUME(x)                     ((x) <= 100U)

/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/
static uint8_t u8InputDevice  = 0U;
static uint8_t u8OutputDevice = 0U;

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup WM8731_Global_Functions WM8731 Global Functions
 * @{
 */

/**
 * @brief  Reset WM8731.
 * @param  None
 * @retval None
 */
void WM8731_Reset(void)
{
    /* Writing 00000000 to register resets device */
    BSP_I2C_WM8731_Write(WM8731_R15_RST, 0U);
    u8InputDevice  = 0U;
    u8OutputDevice = 0U;
}

/**
 * @brief  Initialize WM8731.
 * @param  [in] pstcWm8731Init          Pointer to a @ref stc_wm8731_init_t structure
 * @retval An en_result_t enumeration value:
 *           - Ok: Initialize success
 *           - ErrorInvalidParameter: Invalid parameter
 */
en_result_t WM8731_Init(const stc_wm8731_init_t *pstcWm8731Init)
{
    en_result_t enRet = Ok;
    uint16_t u16AudioPath;
    uint16_t u16PowerControl;

    if (NULL == pstcWm8731Init)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* Check parameters */
        DDL_ASSERT(IS_WM8731_INPUT_DEVICE(pstcWm8731Init->u8InputDevice));
        DDL_ASSERT(IS_WM8731_OUTPUT_DEVICE(pstcWm8731Init->u8OutputDevice));
        DDL_ASSERT(IS_WM8731_OUTPUT_SRC(pstcWm8731Init->u8OutputSrc));
        DDL_ASSERT(IS_WM8731_AUDIO_FREQ(pstcWm8731Init->u32AudioFreq));
        DDL_ASSERT(IS_WM8731_VOLUME(pstcWm8731Init->u8Volume));
        DDL_ASSERT(IS_WM8731_DATA_FORMAT(pstcWm8731Init->u8DataForamt));
        DDL_ASSERT(IS_WM8731_DATA_LEN(pstcWm8731Init->u8DataLength));

        /* Initialize the control interface of the codec */
        BSP_I2C_WM8731_Init();
        /* Writing 00000000 to register resets device */
        BSP_I2C_WM8731_Write(WM8731_R15_RST, 0U);
        BSP_I2C_WM8731_Delay(50U);

        u8InputDevice  = pstcWm8731Init->u8InputDevice;
        u8OutputDevice = pstcWm8731Init->u8OutputDevice;
        /* Path Configurations for input */
        if (WM8731_INPUT_DEVICE_NONE != u8InputDevice)
        {
            switch (u8InputDevice)
            {
                case WM8731_INPUT_DEVICE_MICROPHONE:
                    u16AudioPath    = 0x05U;
                    u16PowerControl = 0x01U;
                    break;
                case WM8731_INPUT_DEVICE_LINE:
                    u16AudioPath    = 0x02U;
                    u16PowerControl = 0x02U;
                    break;
                default:
                    /* Invalid input device */
                    u16AudioPath    = 0x02U;
                    u16PowerControl = 0x07U;
                    break;
            }
        }
        else
        {
            u16AudioPath    = 0x02U;
            u16PowerControl = 0x07U;
        }

        u16AudioPath |= pstcWm8731Init->u8OutputSrc;
        /* Path Configurations for output */
        if (WM8731_OUTPUT_DEVICE_NONE != u8OutputDevice)
        {
            switch (u8OutputDevice)
            {
                case WM8731_OUTPUT_DEVICE_LINE:
                case WM8731_OUTPUT_DEVICE_HEADPHONE:
                case WM8731_OUTPUT_DEVICE_BOTH:
                    break;
                default:
                    u16PowerControl |= 0x08U;
                    break;
            }
        }
        else
        {
            u16PowerControl |= 0x08U;
        }
        /* Power down control */
        BSP_I2C_WM8731_Write(WM8731_R6_PWR_DOWN, (u16PowerControl | 0x10U));
        /* Analogue audio path control */
        BSP_I2C_WM8731_Write(WM8731_R4_ANA_AUDIO_PATH, u16AudioPath);
        /* Set audio frequency and volume */
        WM8731_SetAudioFreq(pstcWm8731Init->u32AudioFreq);
        WM8731_SetVolume(pstcWm8731Init->u8Volume);
        /* Configure digital audio interface format, Slave mode */
        BSP_I2C_WM8731_Write(WM8731_R7_DIG_AUDIO_IF, ((uint16_t)pstcWm8731Init->u8DataForamt | pstcWm8731Init->u8DataLength));

        /* Active Control */
        BSP_I2C_WM8731_Write(WM8731_R9_ACT, 0x01U);
        /* Enable the DAC signal path */
        BSP_I2C_WM8731_Write(WM8731_R6_PWR_DOWN, u16PowerControl);
    }

    return enRet;
}

/**
 * @brief  Start the audio codec play.
 * @param  None
 * @retval None
 */
void WM8731_Play(void)
{
    /* Unmute the output */
    if (WM8731_OUTPUT_DEVICE_NONE != u8OutputDevice)
    {
        /* Disable DAC soft mute */
        BSP_I2C_WM8731_Write(WM8731_R5_DIG_AUDIO_PATH, 0x00U);
    }
}

/**
 * @brief  Stop the audio codec play.
 * @param  None
 * @retval None
 */
void WM8731_Stop(void)
{
    /* Mute the output */
    if (WM8731_INPUT_DEVICE_NONE != u8OutputDevice)
    {
        /* Enable DAC soft mute */
        BSP_I2C_WM8731_Write(WM8731_R5_DIG_AUDIO_PATH, 0x08U);
    }
}

/**
 * @brief  Set the new audio frequency.
 * @param  [in] u32Freq                 The audio frequency
 *         This parameter can be one of the following values:
 *           @arg WM8731_AUDIO_FREQ_8K
 *           @arg WM8731_AUDIO_FREQ_32K
 *           @arg WM8731_AUDIO_FREQ_48K
 *           @arg WM8731_AUDIO_FREQ_96K
 * @retval None
 */
void WM8731_SetAudioFreq(uint32_t u32Freq)
{
    /* Check parameters */
    DDL_ASSERT(IS_WM8731_AUDIO_FREQ(u32Freq));

    /* Base Over-Sampling Rate = 256fs */
    switch (u32Freq)
    {
        case WM8731_AUDIO_FREQ_8K:
            BSP_I2C_WM8731_Write(WM8731_R8_SPL, 0x0CU);
            break;
        case WM8731_AUDIO_FREQ_32K:
            BSP_I2C_WM8731_Write(WM8731_R8_SPL, 0x18U);
            break;
        case WM8731_AUDIO_FREQ_48K:
            BSP_I2C_WM8731_Write(WM8731_R8_SPL, 0x00U);
            break;
        case WM8731_AUDIO_FREQ_96K:
            BSP_I2C_WM8731_Write(WM8731_R8_SPL, 0x1CU);
            break;
        default:
            /* Sample Rate = 48 (KHz) */
            BSP_I2C_WM8731_Write(WM8731_R8_SPL, 0x18U);
            break;
    }
}

/**
 * @brief  Set the codec volume level.
 * @param  [in] u8Volume                The codec volume level
 *         This parameter can be a value between Min_Data = 0 and Max_Data = 100
 * @retval None
 */
void WM8731_SetVolume(uint8_t u8Volume)
{
    uint8_t u8ConvVolume;

    /* Check parameters */
    DDL_ASSERT(IS_WM8731_VOLUME(u8Volume));

    /* Input volume */
    if (WM8731_INPUT_DEVICE_LINE == u8InputDevice)
    {
        u8ConvVolume = WM8731_INPUT_VOL_CONV(u8Volume);
        /* Mute line input */
        if (0U == u8ConvVolume)
        {
            BSP_I2C_WM8731_Write(WM8731_R0_LEFT_LINE_IN, 0x80U);
            BSP_I2C_WM8731_Write(WM8731_R1_RIGHT_LINE_IN, 0x80U);
        }
        else
        {
            BSP_I2C_WM8731_Write(WM8731_R0_LEFT_LINE_IN, (uint16_t)u8ConvVolume);
            BSP_I2C_WM8731_Write(WM8731_R1_RIGHT_LINE_IN, (uint16_t)u8ConvVolume);
        }
    }

    /* Output volume */
    if (WM8731_OUTPUT_DEVICE_HEADPHONE == (u8OutputDevice & WM8731_OUTPUT_DEVICE_HEADPHONE))
    {
        u8ConvVolume = WM8731_OUTPUT_VOL_CONV(u8Volume) + 0x2FU;
        if(u8ConvVolume > 0x2FU)
        {
            BSP_I2C_WM8731_Write(WM8731_R2_LEFT_HP_OUT, (uint16_t)u8ConvVolume);
            BSP_I2C_WM8731_Write(WM8731_R3_RIGHT_HP_OUT, (uint16_t)u8ConvVolume);
        }
        else
        {
            /* Mute headphone output  */
            BSP_I2C_WM8731_Write(WM8731_R2_LEFT_HP_OUT, 0x00U);
            BSP_I2C_WM8731_Write(WM8731_R3_RIGHT_HP_OUT, 0x00U);
        }
    }
}

/**
 * @}
 */

#endif /* BSP_WM8731_ENABLE */

/**
 * @}
 */

/**
* @}
*/

/**
* @}
*/

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
